#include "Light.h"
#include "Mesh.h"


Light::Light(	float diff_r, float diff_g, float diff_b, float spec_r, float spec_g, float spec_b, 
				float dir_x, float dir_y, float dir_z, float pos_x, float pos_y, float pos_z, 
				float att_x, float att_y, float att_z, float cutoff, float exp)						
				
				
				:	shadow_size(1024), bHasShadows(true),_fx_shadowMap(0), fx_m_L_WorldViewProj(0), _device(0), 
					_context(0), tex_shadow(0), dsv_shadow(0), srv_shadow(0), rtv(0), dsv(0), vp()
{
	light.attenuation = XMFLOAT4(att_x, att_y, att_z, 1.0f);
	light.diffuse = XMFLOAT4(diff_r, diff_g, diff_b, exp);
	light.specular = XMFLOAT4(spec_r, spec_g, spec_b, cutoff);
	light.direction = XMFLOAT4(dir_x, dir_y, dir_z, 0.0f);
	light.position = XMFLOAT4(pos_x, pos_y, pos_z, 1.0f);
}
 

Light::~Light()
{
	try
	{
		if (_fx_shadowMap) {
			_fx_shadowMap->Release();
		}
		
		if (_device)	
			_device->Release();
		
		if (_context)
			_context->Release();
		
		if (tex_shadow)
			tex_shadow->Release();
		
		if (dsv_shadow)
			dsv_shadow->Release();

		if (srv_shadow)
			srv_shadow->Release();
		
		if (rtv)
			rtv->Release();

		if (dsv)
			dsv->Release();
	}
	catch(...)
	{
		MessageBoxA(0, "exception thrown in  destructor", 0, 0);
	}
}


void Light::startShadowMapDraw()
{
	// save rtv, dsv, viewport
	_context->OMGetRenderTargets(1, &rtv, &dsv);
	UINT num_vps = 1;
	_context->RSGetViewports(&num_vps, &vp);
	
	///// set ...
	ID3D11RenderTargetView* rt_empty[1] = { 0 };
	
	// depth stencil view bind-clear
	_context->OMSetRenderTargets(1, rt_empty, dsv_shadow); 
	_context->ClearDepthStencilView(dsv_shadow, D3D11_CLEAR_DEPTH, 1.0f, 0xff);

	D3D11_VIEWPORT vp_shadow;
	ZeroMemory(&vp_shadow, sizeof(vp_shadow));
	vp_shadow.Height = (float)shadow_size;
	vp_shadow.Width = (float)shadow_size;
	vp_shadow.MinDepth = 0.0f;
	vp_shadow.MaxDepth = 1.0f;
	
	_context->RSSetViewports(1, &vp_shadow);	
	////
	fx_m_L_WorldViewProj = _fx_shadowMap->GetVariableByName("m_L_WorldViewProj")->AsMatrix();

}

void Light::endShadowMapDraw()
{
	_context->OMSetRenderTargets(1, &rtv, dsv);
	_context->RSSetViewports(1, &vp);

	rtv->Release();
	dsv->Release();
}

Light::Light()	:	shadow_size(1024), bHasShadows(true),_fx_shadowMap(0), fx_m_L_WorldViewProj(0), _device(0), _context(0), 
					tex_shadow(0), dsv_shadow(0), srv_shadow(0), rtv(0), dsv(0), vp()
{
	
}

XMMATRIX Light::getViewProjMatrix() const
{

		XMVECTOR to  = XMLoadFloat3(&(XMFLOAT3(light.direction.x, light.direction.y, light.direction.z)));	
		XMVECTOR eye;
		
		if (abs(light.attenuation.w - Light::LIGHT_SPOT) < 0.001f)
			eye = XMLoadFloat4(&light.position);
		else
			eye = -XMVector4Normalize(to) * 5.0f;

		
		
		XMVECTOR up = XMVector3Orthogonal(to);
		XMMATRIX m_view = XMMatrixLookToLH(eye, to, up);

		XMMATRIX m_proj = XMMatrixOrthographicLH(10.0f, 10.0f, 0.5f, 20.0f);

		return m_view * m_proj;
	

}


void Light::drawShadowMap(const Mesh* mesh)
{
	if (mesh->isShadowCaster()) 
	{
		for (int i=0; i<mesh->getNumSubsets(); i++)
		{
			XMMATRIX m1 = mesh->getWorldMatrix(i);
			XMMATRIX m2 = getViewProjMatrix();

			XMMATRIX m_L_WVP = mesh->getWorldMatrix(i) * getViewProjMatrix();
			fx_m_L_WorldViewProj->SetMatrix((float*)&m_L_WVP);

			mesh->draw(0, this->_fx_shadowMap, "Shadow_Mapping");
		}
	}
}

void Light::createShadowResources(int w, ID3D11Device* g_dev, ID3D11DeviceContext* g_context)
{

	shadow_size = w;

	_device = g_dev;
	_device->AddRef();

	_context = g_context;
	_context->AddRef();

	// 1. Create Texture
	D3D11_TEXTURE2D_DESC tex_desc;
	ZeroMemory(&tex_desc, sizeof(tex_desc));
	tex_desc.Width = shadow_size;
	tex_desc.Height = shadow_size;
	tex_desc.MipLevels = 1;
	tex_desc.ArraySize = 1;
	tex_desc.Format = DXGI_FORMAT_R32_TYPELESS;
	tex_desc.SampleDesc.Count = 1; tex_desc.SampleDesc.Quality = 0;
	tex_desc.Usage = D3D11_USAGE_DEFAULT;
	tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE |  D3D11_BIND_DEPTH_STENCIL;
	tex_desc.CPUAccessFlags = tex_desc.MiscFlags = 0;

	HRESULT hr = g_dev->CreateTexture2D(&tex_desc, 0, &tex_shadow);
	if (FAILED(hr)) MessageBoxA(0, "Error create shadow texture", 0, 0);

	// 2. Create Render Target View
	D3D11_DEPTH_STENCIL_VIEW_DESC  dsv_desc;
	ZeroMemory(&dsv_desc, sizeof(dsv_desc));
	dsv_desc.Format = DXGI_FORMAT_D32_FLOAT;
	dsv_desc.Flags = 0;
	dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
	dsv_desc.Texture2D.MipSlice = 0;

	hr = g_dev->CreateDepthStencilView(tex_shadow, &dsv_desc, &dsv_shadow);
	if (FAILED(hr)) MessageBoxA(0, "Error creating shadow dsv", 0, 0);

	// 3. Create Shader Resource View
	D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
	ZeroMemory(&srv_desc, sizeof(srv_desc));
	srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
	srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srv_desc.Texture2D.MipLevels = 1;
	srv_desc.Texture2D.MostDetailedMip = 0;
	
	hr = g_dev->CreateShaderResourceView(tex_shadow, &srv_desc, &srv_shadow);
	if (FAILED(hr)) MessageBoxA(0, "Error creating shadow srv", 0, 0);
	
	






}
